package com.niiwoo.civet.trade.service.local.invest;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.niiwoo.civet.account.dto.request.InvestPayDubboRequestDTO;
import com.niiwoo.civet.account.dto.response.AccountBaseResponse;
import com.niiwoo.civet.account.service.AccountDubboService;
import com.niiwoo.civet.account.service.AccountInvestDubboService;
import com.niiwoo.civet.base.enums.DeadlineUnitEnum;
import com.niiwoo.civet.base.enums.UserInvestConfigDetailTypeEnum;
import com.niiwoo.civet.trade.constant.InvestPriorityWeight;
import com.niiwoo.civet.trade.constant.RabbitConstant;
import com.niiwoo.civet.trade.constant.TradeRedisKey;
import com.niiwoo.civet.trade.dao.entity.*;
import com.niiwoo.civet.trade.dao.mapper.*;
import com.niiwoo.civet.trade.dto.UserInvestConfigDetailDTO;
import com.niiwoo.civet.trade.dto.common.PeriodValue;
import com.niiwoo.civet.trade.dto.common.SysInvestConfigDTO;
import com.niiwoo.civet.trade.dto.request.AutoInvestSettingRequestDTO;
import com.niiwoo.civet.trade.dto.request.BatchInvestRequestDTO;
import com.niiwoo.civet.trade.dto.request.InvestDubboRequest;
import com.niiwoo.civet.trade.dto.request.InvestPriorityChangeRequest;
import com.niiwoo.civet.trade.dto.response.InvestOrderResponseDTO;
import com.niiwoo.civet.trade.dto.response.invest.ProjectStartInvestAndIncreResponseDTO;
import com.niiwoo.civet.trade.enums.*;
import com.niiwoo.civet.trade.service.local.common.InvestCommonServer;
import com.niiwoo.civet.trade.service.local.contract.FadadaContractContext;
import com.niiwoo.civet.trade.service.local.myloan.MyLoanLocalService;
import com.niiwoo.civet.trade.utils.Calculator;
import com.niiwoo.tripod.base.helper.StringHelper;
import com.niiwoo.tripod.base.utils.BigDecimals;
import com.niiwoo.tripod.lanmao.enums.UserRoleEnum;
import com.niiwoo.tripod.provider.exception.ProjectInvestedException;
import com.niiwoo.tripod.provider.exception.ProjectInvestingException;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Created by zhangwanli on 2018/4/25.
 */
@Service
@Slf4j
public class AutoBatchInvestService {

	@Autowired
	private UserInvestConfigMapperExt userInvestConfigMapperExt;
	@Autowired
	private UserInvestPrizeConfigMapperExt userInvestPrizeConfigMapperExt;
	@Autowired
	private ProjectInvestingMapperExt projectInvestingMapperExt;
	@Autowired
	private ProjectContractMapperExt projectContractMapperExt;
	@Autowired
	private RedisTemplate<String, String> redisTemplate;
	@Autowired
	private SnowflakeIdWorker snowflakeIdWorker;
	@Autowired
	private FadadaContractContext fadadaContractContext;
	@Autowired
	private InvestQueueService investQueueService;
	@Autowired
	private UserInvestConfigDetailMapperExt userInvestConfigDetailMapperExt;
	@Autowired
	private AutoAndBatchInvestConfigMapperExt autoAndBatchInvestConfigMapperExt;
	@Autowired
	private InvestService investService;
	@Autowired
	private InvestPrizeService investPrizeService;
	@Reference(version = "1.0.0")
	private AccountDubboService accountDubboService;
	@Reference(version = "1.0.0")
	private AccountInvestDubboService accountInvestDubboService;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private SysInvestConfigMapperExt sysInvestConfigMapperExt;
    @Autowired
    private InvestCommonServer investCommonServer;
	@Autowired
	private MyLoanLocalService myLoanLocalService;
    
	/**
	 * 使用红包最低可用余额，依据系统红包规则：使用红包金额&比例得出
	 */
	private FastDateFormat dayFormat = FastDateFormat.getInstance("yyyyMMdd");
	// 100起投
	private final BigDecimal hundred = BigDecimal.valueOf(100);
	private static final String COMMISSION_AUTH_CONTRACT_SEQ = "auto-invest-commission-auth-contract-seq-";

	@Transactional
	public void saveAutoInvestSetting(AutoInvestSettingRequestDTO dto) {
		String userId = dto.getUserId();
		UserInvestConfig config = new UserInvestConfig();
		config.setUserId(userId);
		config.setInvestWay(InvestWayEnum.AUTO.getValue());
		config.setSingleMaxInvestAmount(dto.getSingleMaxInvestAmount());
		config.setMinKeepBalance(dto.getMinKeepBalance());
		config.setEnableAuto((byte) 1);
		config.setEnableSystem((byte) 0);

		UserInvestConfig existUserCfg = userInvestConfigMapperExt.queryByUserId(userId, InvestWayEnum.AUTO.getValue());
		Byte weight = InvestPriorityWeight.NORMAL;
		// 变更用户投资配置，权重使用原始值，不存在使用默认值
		if (Objects.isNull(existUserCfg)) {
			config.setWeight(weight);
			config.setId(snowflakeIdWorker.nextId());
			config.setCreateTime(new Date());
			userInvestConfigMapperExt.insertSelective(config);
		} else {
			log.info("用户{}保存自动投资配置，原始配置项为{}", userId, JSON.toJSONString(existUserCfg));
			config.setWeight(existUserCfg.getWeight());
			config.setId(existUserCfg.getId());
			config.setUpdateTime(new Date());
			List<UserInvestConfigDetail> userInvestConfigDetail = userInvestConfigDetailMapperExt.findByUserInvestConfigId(existUserCfg.getId());
			if (Objects.nonNull(userInvestConfigDetail)) {
				log.info("用户{}保存自动投资配置，原始配置细项配置{}", userId, JSON.toJSONString(userInvestConfigDetail));
			}
			userInvestConfigMapperExt.updateByPrimaryKeySelective(config);
		}
		// 保存自动投资配置细项,每次删除重新插入
		userInvestConfigDetailMapperExt.deleteByUserInvestConfigId(config.getId());
		// 产品类型
		List<UserInvestConfigDetail> productDetails = insertUserInvestConfigDetail(config.getId(), dto.getProductTypes(), UserInvestConfigDetailTypeEnum.PRODUCT_TYPE.getValue());
		// 还款方式
		List<UserInvestConfigDetail> repaymentDetails = insertUserInvestConfigDetail(config.getId(), dto.getRepaymentTypes(), UserInvestConfigDetailTypeEnum.REPAYMENT_TYPE.getValue());
		// 期限
		List<UserInvestConfigDetail> periodDetail = insertUserInvestConfigDetailPeriodType(config.getId(), dto.getPeriodTypes(), UserInvestConfigDetailTypeEnum.PERIOD_TYPE.getValue());
		// 利率
		List<UserInvestConfigDetail> rateDetail = insertUserInvestConfigDetailRateType(config.getId(), dto.getRatetTypes(), UserInvestConfigDetailTypeEnum.RATE_TYPE.getValue());

		UserInvestPrizeConfig prizeConfig = userInvestPrizeConfigMapperExt.queryByUserId(userId, InvestWayEnum.AUTO.getValue());
		if (prizeConfig != null) {
			log.info("previous auto invest prize config:{}", JSON.toJSONString(prizeConfig));
			prizeConfig.setIsAutoUsePrize(dto.getIsAutoUsePrize());
			prizeConfig.setIsMustUsePrize(dto.getIsMustUsePrize());
			prizeConfig.setUseCouponMinInvestAmount(dto.getUseCouponMinInvestAmount());
			userInvestPrizeConfigMapperExt.updateByPrimaryKeySelective(prizeConfig);
		} else {
			prizeConfig = new UserInvestPrizeConfig();
			prizeConfig.setId(snowflakeIdWorker.nextId());
			prizeConfig.setUserId(userId);
			prizeConfig.setInvestWay(InvestWayEnum.AUTO.getValue());
			prizeConfig.setIsAutoUsePrize(dto.getIsAutoUsePrize());
			prizeConfig.setIsMustUsePrize(dto.getIsMustUsePrize());
			prizeConfig.setUseCouponMinInvestAmount(dto.getUseCouponMinInvestAmount());
			prizeConfig.setCreateTime(new Date());
			userInvestPrizeConfigMapperExt.insertSelective(prizeConfig);
		}
		productDetails.addAll(rateDetail);
		productDetails.addAll(periodDetail);
		productDetails.addAll(repaymentDetails);
		// 用户配置存队列
		userIntoAutoQueue(userId, weight, productDetails);
	}

	/**
	 * 用户放入队列
	 * 
	 * @param userId
	 * @param weight
	 * @param productTypes
	 * @param periodTypes
	 * @param repaymentTypes
	 * @param ratetTypes
	 */
	public void userIntoAutoQueue(String userId, Byte weight, List<UserInvestConfigDetail> details) {
		// 参数都不可空
		if (CollectionUtils.isEmpty(details) || Objects.isNull(weight) || Objects.isNull(userId)) {
			return;
		}
		//删除该用户原先的配置
		removeAutoInvestUser(userId);
		// 权重
		double prior = Calculator.investPriority(weight);
		
		for (UserInvestConfigDetail userInvestConfigDetail : details) {
			// 获取队列名称
			String queueName = generateAutoInvestQueueName(userInvestConfigDetail.getConfigType(), userInvestConfigDetail.getConfigValue(), userInvestConfigDetail.getOtherConfigValue());
			log.info("用户{}，进入投资队列{}",userId,queueName);
			// 存入队列
			redisTemplate.boundZSetOps(queueName).add(userId, prior);
		}

	}

	private void removeAutoInvestUser(String userId) {
		Set<String> keys = redisTemplate.keys(TradeRedisKey.AUTO_INVESTORS_QUEUE_PREFIX + "*");
		if (CollectionUtils.isNotEmpty(keys)) {
			for (String key : keys) {
				redisTemplate.boundZSetOps(key).remove(userId);
			}
		}
	}

	/**
	 * 
	 * @param userInvestConfigId
	 *            自动投资配置主键
	 * @param ratetTypes
	 *            细项利率参数值
	 * @param type
	 *            细项参数类型
	 */
	private List<UserInvestConfigDetail> insertUserInvestConfigDetailRateType(Long userInvestConfigId, List<BigDecimal> ratetTypes, Byte type) {
		List<UserInvestConfigDetail> details = new ArrayList<>();
		for (BigDecimal rate : ratetTypes) {
			UserInvestConfigDetail configDetail = new UserInvestConfigDetail();
			configDetail.setId(snowflakeIdWorker.nextId());
			configDetail.setConfigType(type);
			configDetail.setCreateTime(new Date());
			configDetail.setUpdateTime(new Date());
			configDetail.setUserInvestConfigId(userInvestConfigId);
			//IOS精度异常，规范精度。神盾只支持4位数输入，处理成四位小数
			configDetail.setConfigValue(rate.setScale(4, RoundingMode.FLOOR).stripTrailingZeros().toPlainString());
			userInvestConfigDetailMapperExt.insertSelective(configDetail);
			details.add(configDetail);
		}
		return details;
	}

	/**
	 * 
	 * @param userInvestConfigId
	 *            自动投资配置主键
	 * @param periodTypes
	 *            细项期限参数值
	 * @param type
	 *            细项参数类型
	 */
	private List<UserInvestConfigDetail> insertUserInvestConfigDetailPeriodType(Long userInvestConfigId, List<PeriodValue> periodTypes, Byte type) {
		List<UserInvestConfigDetail> details = new ArrayList<>();
		for (PeriodValue periodValue : periodTypes) {
			UserInvestConfigDetail configDetail = new UserInvestConfigDetail();
			configDetail.setId(snowflakeIdWorker.nextId());
			configDetail.setConfigType(type);
			configDetail.setCreateTime(new Date());
			configDetail.setUpdateTime(new Date());
			configDetail.setUserInvestConfigId(userInvestConfigId);
			configDetail.setConfigValue(periodValue.getDeadline().toString());
			configDetail.setOtherConfigValue(periodValue.getDeadlineUnit().toString());
			userInvestConfigDetailMapperExt.insertSelective(configDetail);
			details.add(configDetail);
		}
		return details;
	}

	/**
	 * 
	 * @param userInvestConfigId
	 *            自动投资配置主键
	 * @param params
	 *            细项参数值
	 * @param type
	 *            细项参数类型
	 */
	private List<UserInvestConfigDetail> insertUserInvestConfigDetail(Long userInvestConfigId, List<Integer> params, Byte type) {
		List<UserInvestConfigDetail> user = new ArrayList<>();
		for (Integer value : params) {
			UserInvestConfigDetail configDetail = new UserInvestConfigDetail();
			configDetail.setId(snowflakeIdWorker.nextId());
			configDetail.setConfigType(type);
			configDetail.setCreateTime(new Date());
			configDetail.setUpdateTime(new Date());
			configDetail.setUserInvestConfigId(userInvestConfigId);
			configDetail.setConfigValue(value.toString());
			userInvestConfigDetailMapperExt.insertSelective(configDetail);
			user.add(configDetail);
		}
		return user;
	}

	@Transactional
	public void saveBatchInvestSetting(BatchInvestRequestDTO dto) {
		String userId = dto.getUserId();
		UserInvestConfig config = new UserInvestConfig();
		config.setUserId(userId);
		config.setInvestWay(InvestWayEnum.BATCH.getValue());
		config.setSingleMaxInvestAmount(dto.getSingleMaxInvestAmount());
		config.setMinKeepBalance(dto.getMinKeepBalance());
		config.setSyncToAuto(dto.getSyncToAutoInvest());
		config.setEnableSystem((byte) 0);
		config.setReadFlag(Byte.valueOf("1"));
		UserInvestConfig existUserCfg = userInvestConfigMapperExt.queryByUserId(userId, InvestWayEnum.BATCH.getValue());
		if (existUserCfg != null) {
			config.setId(existUserCfg.getId());
			userInvestConfigMapperExt.updateByUserId(config);
		} else {
			config.setId(snowflakeIdWorker.nextId());
			config.setCreateTime(new Date());
			userInvestConfigMapperExt.insertSelective(config);
		}
		// 保存批量投资配置细项,每次删除重新插入
		userInvestConfigDetailMapperExt.deleteByUserInvestConfigId(config.getId());
		// 产品类型
		insertUserInvestConfigDetail(config.getId(), dto.getProductTypes(), UserInvestConfigDetailTypeEnum.PRODUCT_TYPE.getValue());
		// 还款方式
		insertUserInvestConfigDetail(config.getId(), dto.getRepaymentTypes(), UserInvestConfigDetailTypeEnum.REPAYMENT_TYPE.getValue());
		// 期限
		insertUserInvestConfigDetailPeriodType(config.getId(), dto.getPeriodTypes(), UserInvestConfigDetailTypeEnum.PERIOD_TYPE.getValue());
		// 利率
		insertUserInvestConfigDetailRateType(config.getId(), dto.getRatetTypes(), UserInvestConfigDetailTypeEnum.RATE_TYPE.getValue());

		UserInvestPrizeConfig prizeConfig = userInvestPrizeConfigMapperExt.queryByUserId(userId, InvestWayEnum.BATCH.getValue());
		if (prizeConfig != null) {
			prizeConfig.setIsAutoUsePrize(dto.getIsAutoUsePrize());
			prizeConfig.setIsMustUsePrize(dto.getIsMustUsePrize());
			prizeConfig.setUseCouponMinInvestAmount(dto.getUseCouponMinInvestAmount());
			userInvestPrizeConfigMapperExt.updateByPrimaryKeySelective(prizeConfig);
		} else {
			prizeConfig = new UserInvestPrizeConfig();
			prizeConfig.setId(snowflakeIdWorker.nextId());
			prizeConfig.setUserId(userId);
			prizeConfig.setInvestWay(InvestWayEnum.BATCH.getValue());
			prizeConfig.setIsAutoUsePrize(dto.getIsAutoUsePrize());
			prizeConfig.setIsMustUsePrize(dto.getIsMustUsePrize());
			prizeConfig.setUseCouponMinInvestAmount(dto.getUseCouponMinInvestAmount());
			prizeConfig.setCreateTime(new Date());
			userInvestPrizeConfigMapperExt.insertSelective(prizeConfig);
		}
	}

	@Transactional
	public void disableAllAutoInvest() {
		//系统关闭，清空所有的配置，包括批量投资
		//开启自动投资的改为未读，关闭着的改为已读
		userInvestConfigMapperExt.disableInvest();
		userInvestConfigMapperExt.systemCloseAutoInvest();
		
	}

	public List<SysInvestConfigDTO> querySysInvestConfigDTOList() {
		//查询所有的系统投资配置方案 
		List<AutoAndBatchInvestConfig> sysCfgs = querySysInvestConfigList();
		if (Objects.isNull(sysCfgs)) {
			return new ArrayList<>();
		}
		return sysCfgs.stream().map(cfg -> {
			SysInvestConfigDTO sysConfig = new SysInvestConfigDTO();
			sysConfig.setId(cfg.getId());
			sysConfig.setLoanType(cfg.getProjectType());
			sysConfig.setPeriod(cfg.getDeadline());
			sysConfig.setPeriodType(cfg.getDeadlineUnit());
			sysConfig.setRateBegin(cfg.getInvestRate());
			sysConfig.setRateEnd(cfg.getInvestRate());
			sysConfig.setRepaymentType(cfg.getProfitType());
			return sysConfig;
		}).collect(Collectors.toList());
	}

	public List<AutoAndBatchInvestConfig> querySysInvestConfigList() {
		return autoAndBatchInvestConfigMapperExt.selectAll();
	}

	public List<UserInvestConfig> queryAllUserInvestConfig(InvestWayEnum investWay) {
		return userInvestConfigMapperExt.queryAllUserInvestConfig(investWay.getValue());
	}
	public UserInvestConfig queryUserInvestConfig(String userId, InvestWayEnum investWay) {
		return userInvestConfigMapperExt.queryByUserId(userId, investWay.getValue());
	}

	@Transactional
	public UserInvestConfig queryUserInvestConfigForMaster(String userId, InvestWayEnum investWay) {
		return userInvestConfigMapperExt.queryByUserId(userId, investWay.getValue());
	}
	
	
	public UserInvestPrizeConfig queryUserInvestPrizeConfig(String userId, InvestWayEnum investWay) {
		return userInvestPrizeConfigMapperExt.queryByUserId(userId, investWay.getValue());
	}

	public List<ProjectInvesting> queryAllInvestingProjectWithLimit(int limit) {
		return projectInvestingMapperExt.selectAllWithLimit(limit);
	}

	/**
	 * 保存法大大委托授权协议书
	 */
	@Transactional
	public ProjectContract initCommissionAuthAgreement(String userId, String contractNoPrefix, FadadaContractEnum.TradeTypeEnum tradeType) {
		String format = dayFormat.format(new Date());
		String key = COMMISSION_AUTH_CONTRACT_SEQ + format;
		Long value = redisTemplate.opsForValue().increment(key, 1);
		if (value == 1) {
			redisTemplate.expire(key, 24, TimeUnit.HOURS);
		}
		String paddingValue = StringHelper.lpadding(4, value);
		// NWZD（NWPL）+ YYYYMMDD + 4位序号(0001) + 授权协议
		String contractNo = contractNoPrefix + format + paddingValue + "授权协议";
		Byte contractType = FadadaContractTemplateEnum.TemplateTypeEnum.CONSIGNOR_AUTHORIZE_PROTOCOL.getType();
		ProjectContract contract = fadadaContractContext.initContractRecord(null, userId, contractNo, contractType, tradeType.getType(), null);
		projectContractMapperExt.insertSelective(contract);
		return contract;
	}

	@Transactional
	public void disableAutoInvest(String userId) {

		UserInvestConfig userInvestConfig = userInvestConfigMapperExt.queryByUserId(userId, InvestWayEnum.AUTO.getValue());
		if (Objects.nonNull(userInvestConfig)) {
			UserInvestConfig userInvestConfigUpdate = new UserInvestConfig();
			userInvestConfigUpdate.setId(userInvestConfig.getId());
			userInvestConfigUpdate.setEnableAuto(Byte.valueOf("0"));
			userInvestConfigUpdate.setUpdateTime(new Date());
			// 保留历史数据，做变更操作
			userInvestConfigMapperExt.updateByPrimaryKeySelective(userInvestConfigUpdate);
		}

		// 删除Redis中投资人zset集合中的该用户
		investQueueService.discard(userId);
		log.info("用户取消自动投资,userId={}", userId);
	}

	/**
	 * 批量修改用户投资级别
	 *
	 * @param changeList
	 */
	public void changeAutoInvestPriority(List<InvestPriorityChangeRequest> changeList) {
		Set<String> keys = redisTemplate.keys(TradeRedisKey.AUTO_INVESTORS_QUEUE_PREFIX + "*");
		String userId = null;
		Long index = null;
		for (InvestPriorityChangeRequest userWeight : changeList) {
			// 重新获取权重
			Double weight = Calculator.investPriority(userWeight.getWeight());
			userId = userWeight.getUserId();
			for (String key : keys) {
				// 遍历所有自动投资队列，指定队列存在该用户则更新权重
				index = redisTemplate.opsForZSet().rank(key, userId);
				if (Objects.nonNull(index)) {
					redisTemplate.opsForZSet().add(key, userId, weight);
				}
			}
		}
	}

	/**
	 * 重新加载队列
	 */
	public void reloadAllAutoInvestorQueue() {
		// 查询全部用户
		List<UserInvestConfigDetailDTO> userConfigs = userInvestConfigDetailMapperExt.findAllUserConfig();
		Map<String, BoundZSetOperations<String, String>> opsMap = new HashMap<>();
		Map<String, Double> weightMap = new HashMap<>();
		if (CollectionUtils.isNotEmpty(userConfigs)) {
			for (UserInvestConfigDetailDTO config : userConfigs) {
				// 获取队列名称
				String queueName = generateAutoInvestQueueName(config.getConfigType(), config.getConfigValue(), config.getOtherConfigValue());
				BoundZSetOperations<String, String> ops = opsMap.get(queueName);
				if (Objects.isNull(ops)) {
					ops = redisTemplate.boundZSetOps(queueName);
					opsMap.put(queueName, ops);
				}
				// 获取权重
				Double weight = weightMap.get(config.getUserId());
				if (Objects.isNull(weight)) {
					weight = Calculator.investPriority(config.getWeight());
					weightMap.put(config.getUserId(), weight);
				}
				ops.add(config.getUserId(), weight);
			}

		}
	}

	/**
	 * 根据参数获取对应的自动投资的队列名称
	 * 
	 * @param configType
	 * @param configValue
	 * @param otherConfigValue
	 * @return
	 */
	private String generateAutoInvestQueueName(Byte configType, String configValue, String otherConfigValue) {
		// 队列一级目录
		StringBuilder sb = new StringBuilder(TradeRedisKey.AUTO_INVESTORS_QUEUE_PREFIX);
		// 根据类型生成队列二级目录
		if (UserInvestConfigDetailTypeEnum.PRODUCT_TYPE.getValue().equals(configType)) {
			sb.append(TradeRedisKey.AUTO_INVESTORS_QUEUE_PRODUCT);
			sb.append(configValue);
		} else if (UserInvestConfigDetailTypeEnum.PERIOD_TYPE.getValue().equals(configType)) {
			sb.append(TradeRedisKey.AUTO_INVESTORS_QUEUE_DEANLINE);
			sb.append(configValue).append("~").append(otherConfigValue);
		} else if (UserInvestConfigDetailTypeEnum.RATE_TYPE.getValue().equals(configType)) {
			sb.append(TradeRedisKey.AUTO_INVESTORS_QUEUE_RATE);
			sb.append(configValue);
		} else if (UserInvestConfigDetailTypeEnum.REPAYMENT_TYPE.getValue().equals(configType)) {
			sb.append(TradeRedisKey.AUTO_INVESTORS_QUEUE_REPAYMENT);
			sb.append(configValue);
		} else {
			 
			throw new UnsupportedOperationException(""); 
		}
		return sb.toString();
	}

	/**
	 * 新增系统投资配置时，检查已设置自动投资的用户已选择的期限、利率配置是否与新增的配置匹配，若匹配则需把新配置的id也加入该用户的自动投资配置选择的configIds中
	 *
	 * @param userInvestConfigId
	 *            用户投资配置记录id
	 * @param newSysConfig
	 *            新增系统投资配置
	 * @param sysInvestConfigList
	 *            所有系统投资配置
	 */
	@Transactional
	public void onSysInvestSettingAdded(Long userInvestConfigId, SysInvestConfigDTO newSysConfig, List<SysInvestConfig> sysInvestConfigList) {
		// 5.4.0 系统的配置不会影响用户配置
		return;
	}

	/**
	 * 查询指定用的自动投资配置明细
	 * 
	 * @param id
	 * @return
	 */
	public List<UserInvestConfigDetail> queryUserInvestConfigDetail(Long id) {

		return userInvestConfigDetailMapperExt.findByUserInvestConfigId(id);
	}

	public InvestRoundStatus lookupAndInvest( Long projectId,BoundListOperations<String, String> ops) {
		long time = System.currentTimeMillis();
		//获取第一个人
		String investorId = ops.leftPop();
		if(Objects.isNull(investorId)) {
			//没有投资人
			return InvestRoundStatus.NO_ANY_INVESTOR;
		}
		ProjectInvesting project = investService.queryInvestingProjectInfo(projectId);
		Byte projectStatus = project.getStatus();
        BigDecimal contractAmount = project.getContractAmount();
        BigDecimal investedAmount = project.getInvestedAmount();
		//非可投资状态或标的剩余可投金额小于1元中断自动投资流程，ACK消息
        if (!InvestableProjectStatusEnum.INVESTING.getValue().equals(projectStatus)
                || contractAmount.subtract(investedAmount).compareTo(BigDecimal.ONE) < 0) {
            log.info("自动投资===>标的条件不满足,中断该标的自动投资,projectId={},status={},contractAmount={},investedAmount={}", projectId, projectStatus, contractAmount, investedAmount);
            return InvestRoundStatus.PROJECT_FULL_OR_MISCARRY;
        }
		Byte projectType = project.getType();
		BigDecimal minInvestAmount = BigDecimal.valueOf(100);
		//主库查询自动投资配置
		UserInvestConfig config = ((AutoBatchInvestService)AopContext.currentProxy()).queryUserInvestConfigForMaster(investorId, InvestWayEnum.AUTO);
		///////////////////////////// 未开启自动投资、未开启免密投资、余额不足 进入休息区

		log.info("自动投资===>当前投资人,projectId={},userId={},weight={}", projectId, investorId, config.getWeight());

		// 判断当前自动投资设置是否开启。用户投资条件不满足-->移除投资人队列-->用户关闭自动投资-->30分钟后定时任务触发移回队列，情况下时需要判断
		if (config == null || config.getEnableAuto().intValue() != 1 || config.getEnableSystem().intValue() == 1) {
			removeAutoInvestUser(investorId);
			log.info("自动投资===>用户关闭自动投资,中断该用户自动投资移出队列,projectId={},userId={}", projectId, investorId);
			return InvestRoundStatus.INVESTOR_CAUSE_UNMATCH;
		}
		if (investService.isSelfBorrowProject(investorId, project)) {
			log.info("自动投资===>不能投资自己发起的借款标的,跳过该投资人,projectId={},userId={}", projectId, investorId);
			return InvestRoundStatus.SELF_BORROW_PROJECT;
		}

		// 该投资人是否开启免密投资，没有开启(后来手动关闭)，中断，移除队列，note:关闭免密投资系统会关闭自动投资
		AccountBaseResponse accountInfoNoMaster = accountDubboService.getNiiwooAccount(investorId, UserRoleEnum.INVESTOR);
		//查主库
		AccountBaseResponse accountInfo = accountDubboService.loadAccountByIdFromMaster(accountInfoNoMaster.getAccountId());
		if (accountInfo == null) {
			removeAutoInvestUser(investorId);
			log.info("自动投资===>用户没有投资账户,中断该用户自动投资移出队列,projectId={},userId={}", projectId, investorId);
			return InvestRoundStatus.INVESTOR_CAUSE_UNMATCH;
		}
		if (accountInfo.getTender().intValue() != 1) {
			removeAutoInvestUser(investorId);
			log.info("自动投资===>用户关闭免密投资,中断该用户自动投资移出队列,projectId={},userId={}", projectId, investorId);
			return InvestRoundStatus.INVESTOR_CAUSE_UNMATCH;
		}
		// 账户余额不足、低于最低保留金额，中断，移除队列
		BigDecimal availableBalance = accountInfo.getAvailableAmount();
		// 单次投资最低100，不足100清除出自动投资，进入休息区
		if (availableBalance.compareTo(minInvestAmount) < 0) {
			removeAutoInvestUser(investorId);
			investQueueService.enqueueInvestorLounge(investorId, RestAreaReasonEnum.NOT_SUFFICIENT_FUNDS);
			log.info("自动投资===>用户金额不满足,中断该用户自动投资进入休息区,projectId={},userId={},availableBalance={}", projectId, investorId, availableBalance);
			return InvestRoundStatus.INVESTOR_CAUSE_UNMATCH;
		}
		BigDecimal minKeepBalance = config.getMinKeepBalance();
		// 剩余金额-100 小于 保留金额 进入休息区
		if (availableBalance.subtract(minInvestAmount).compareTo(minKeepBalance) < 0) {
			removeAutoInvestUser(investorId);
			investQueueService.enqueueInvestorLounge(investorId, RestAreaReasonEnum.NOT_SUFFICIENT_FUNDS);
			log.info("自动投资===>用户金额不满足,中断该用户自动投资进入休息区,projectId={},userId={},availableBalance={},minKeepBalance={}", projectId, investorId, availableBalance, minKeepBalance);
			return InvestRoundStatus.INVESTOR_CAUSE_UNMATCH;
		}

		UserInvestPrizeConfig prizeConfig = queryUserInvestPrizeConfig(investorId, InvestWayEnum.AUTO);
		boolean autoUsePrize = prizeConfig.getIsAutoUsePrize().intValue() == 1;
		boolean mustUsePrize = prizeConfig.getIsMustUsePrize().intValue() == 1;
		// 必须使用红包而当前标的不支持红包，跳过
		if (autoUsePrize && mustUsePrize && !investPrizeService.projectSupportPrize(project)) {
			log.info("自动投资===>用户配置必须使用红包该标的不支持红包,跳过该标的自动投资,一下个标的再投,projectId={},type={},userId={}", projectId, projectType, investorId);
			return InvestRoundStatus.SETTINGS_CAUSE_UNMATCH;
		}

		// 获取当前风险等级对应的剩余可投资金额.
		BigDecimal surplusCanInvestAmount = investCommonServer.getSurplusCanInvestAmount(investorId);
		if (surplusCanInvestAmount.compareTo(minInvestAmount) < 0) {
			removeAutoInvestUser(investorId);
			investQueueService.enqueueInvestorLounge(investorId, RestAreaReasonEnum.RISK_FUND);
			log.info("自动投资===>当前投资金额已达该风险类型投资上限,中断该用户自动投资,移除投资人队列,将关闭其自动投资,projectId={},userId={}", projectId, investorId);
			// 发送 mq 当前投资金额已达该风险类型投资上限处理
			investService.notifyInvestStopForEvaluation(investorId, InvestFromTypeEnum.AUTOMATIC_INVEST);
			return InvestRoundStatus.INVEST_STOP_BY_EVALUATION_DEAL;
		}

		// 计算该投资人应投金额
		BigDecimal investAmount = calcInvestAmount(project, availableBalance, config, surplusCanInvestAmount);

		if (BigDecimal.ZERO.compareTo(investAmount) == 0) {
			removeAutoInvestUser(investorId);
			investQueueService.enqueueInvestorLounge(investorId, RestAreaReasonEnum.NOT_SUFFICIENT_FUNDS);
			log.info("自动投资===>用户金额向下取整后不足一百,中断该用户自动投资进入休息区,projectId={},userId={},availableBalance={}", projectId, investorId, availableBalance);
			return InvestRoundStatus.INVESTOR_CAUSE_UNMATCH;
		}
		Long prizeId = null;
		if (autoUsePrize) {
			prizeId = investPrizeService.querySuitablePrize(investorId, project, investAmount, prizeConfig.getUseCouponMinInvestAmount());
			log.info("自动投资===>可用红包,prizeId={},projectId={},userId={},investAmount={}", prizeId, projectId, investorId, investAmount);
		}
		// 必须使用红包而又没有可用红包,红包是否可用与当前投资金额有关
		if (autoUsePrize && mustUsePrize && prizeId == null) {
			// 没有返现红包||（返现红包最低使用金额>可用余额）&&加息券最低使用金额>可用余额 进入休息区）
			validatePrize(investorId, prizeConfig.getUseCouponMinInvestAmount(), availableBalance);
			return InvestRoundStatus.INVESTOR_CAUSE_UNMATCH;
		}
		// 发起投资&异常处理
		InvestDubboRequest investReq = new InvestDubboRequest();
		investReq.setUserId(investorId);
		investReq.setInvestAmount(investAmount);
		investReq.setProjectId(projectId);
		investReq.setPrizeId(prizeId);
		try {
			log.info("自动投资===>执行投资,projectId={},userId={}", projectId, investorId);
			invest(investorId, projectId, InvestWayEnum.AUTO, null, accountInfo, availableBalance, investAmount, investReq);
		} catch (ProjectInvestedException e) {
			// 已满标或流标
			log.error("自动投资===>自动投资执行投资出错,projectId={},userId={}", projectId, investorId, e);
			return InvestRoundStatus.PROJECT_FULL_OR_MISCARRY;
		} catch (ProjectInvestingException e) {
			// 标的暂时已投完
			log.error("自动投资===>自动投资执行投资出错,projectId={},userId={}", projectId, investorId, e);
			return InvestRoundStatus.LOCK_INVESTTING;
		} catch (Exception e) {
			// 系统异常或者用户原因导致
			log.error("自动投资===>自动投资执行投资出错,projectId={},userId={},{}", projectId, investorId, e);
//			removeAutoInvestUser(investorId);
//			investQueueService.enqueueInvestorLounge(investorId, RestAreaReasonEnum.UNKNOW);
			return InvestRoundStatus.INVESTOR_CAUSE_UNMATCH;
		}
//		5.5.5 需求  一个用户一个标单轮投资一次，原始需求优先满足所有标
//		if (config.getWeight() < InvestPriorityWeight.HIGH) {
//			// 普通投资用户投资结束重新刷新权重,将会排在队尾
//			ops.rightPush(investorId);
//		} else {
//			//高权重的排队头
//			 ops.leftPush(investorId);
//		}
		reloadUserInvestPriorty(investorId, config.getId(), config.getWeight());
		//投资未满，重新投资
		log.info("自动投资一次,耗时:{}",System.currentTimeMillis()-time);
		return InvestRoundStatus.INVEST_SUCCESS_NOT_FULL;
	}

	/**
	 * 必须使用红包的用户，可用余额大于红包的最低金额或者投资加息券的最低金额
	 * 
	 * @param investorId
	 * @param useCouponMinInvestAmount
	 * @param availableAmount
	 */
	private void validatePrize(String investorId, BigDecimal useCouponMinInvestAmount, BigDecimal availableAmount) {
		boolean isMatch = investPrizeService.valdatePrize(investorId, useCouponMinInvestAmount, availableAmount);
		// 余额不符合红包投资类型 进入休息队列
		if (!isMatch) {
			removeAutoInvestUser(investorId);
			investQueueService.enqueueInvestorLounge(investorId, RestAreaReasonEnum.NO_RED_ENVELOPES);
		}

	}

	/**
	 * 重新设置用户的权重。删除原先数据，然后变更数据
	 * 
	 * @param investorId
	 */
	private void reloadUserInvestPriorty(String userId, Long configId, Byte weight) {
		userIntoAutoQueue(userId, weight, queryUserInvestConfigDetail(configId));
	}

	public void generateUserConfigDetailForList(List<Byte> productTypes, List<PeriodValue> periodTypes, List<Byte> repaymentTypes, List<BigDecimal> ratetTypes, Long id) {
		List<UserInvestConfigDetail> details = queryUserInvestConfigDetail(id);
		// 封装 标的类型&期限&还款方式&利率
		if (CollectionUtils.isNotEmpty(details)) {
			for (UserInvestConfigDetail userInvestConfigDetail : details) {
				if (UserInvestConfigDetailTypeEnum.PRODUCT_TYPE.getValue().equals(userInvestConfigDetail.getConfigType())) {
					productTypes.add(Byte.valueOf(userInvestConfigDetail.getConfigValue()));
				} else if (UserInvestConfigDetailTypeEnum.PERIOD_TYPE.getValue().equals(userInvestConfigDetail.getConfigType())) {
					PeriodValue periodValue = new PeriodValue();
					periodValue.setDeadline(Integer.valueOf(userInvestConfigDetail.getConfigValue()));
					periodValue.setDeadlineUnit(Byte.valueOf(userInvestConfigDetail.getOtherConfigValue()));
					periodTypes.add(periodValue);
				} else if (UserInvestConfigDetailTypeEnum.RATE_TYPE.getValue().equals(userInvestConfigDetail.getConfigType())) {
					ratetTypes.add(new BigDecimal(userInvestConfigDetail.getConfigValue()));
				} else if (UserInvestConfigDetailTypeEnum.REPAYMENT_TYPE.getValue().equals(userInvestConfigDetail.getConfigType())) {
					repaymentTypes.add(Byte.valueOf(userInvestConfigDetail.getConfigValue()));
				}
			}
		}
	}

	public BigDecimal calcInvestAmount(ProjectInvesting project, BigDecimal availableAmount, UserInvestConfig userConfig, BigDecimal surplusCanInvestAmount) {
		BigDecimal contractAmount = project.getContractAmount();
		BigDecimal investedAmount = project.getInvestedAmount();
		BigDecimal minKeepBalance = userConfig.getMinKeepBalance();
		BigDecimal singleMaxInvestAmount = userConfig.getSingleMaxInvestAmount();
		// 投资金额计算 投资金额=满标金额 使用投资金额，投资金额小于满标金额向下百位取整
		BigDecimal availableInvestAmount = contractAmount.subtract(investedAmount);// 可投资金额
		BigDecimal minInvestAmount = BigDecimals.min(availableAmount.subtract(minKeepBalance), availableInvestAmount, singleMaxInvestAmount, surplusCanInvestAmount).setScale(0, BigDecimal.ROUND_DOWN);
		// 可投金额 ！= 投资金额，投资金额做向下取整百操作
		if (availableInvestAmount.compareTo(minInvestAmount) != 0) {
			minInvestAmount = minInvestAmount.divideAndRemainder(hundred)[0].multiply(hundred);
			log.info("投资金额计算==》投资人{}，标的ID{}，可投金额{},向下取整后投资金额{},账户余额{},单笔投资最大金额{},账户保留金额{}", userConfig.getUserId(), project.getProjectId(), availableInvestAmount, minInvestAmount, availableAmount,
					singleMaxInvestAmount, minKeepBalance);
			ProjectStartInvestAndIncreResponseDTO responseDTO = myLoanLocalService.getProjectStartInvestAndIncrement();
			if (minInvestAmount.compareTo(responseDTO.getStartInvestMoney()) >= 0) {
				BigDecimal incrmentValide = minInvestAmount.subtract(responseDTO.getStartInvestMoney());
				BigDecimal[] WholeHundred = incrmentValide.divideAndRemainder(responseDTO.getIncrementAmount());
				if (BigDecimal.ZERO.compareTo(WholeHundred[1]) != 0) {
					minInvestAmount = minInvestAmount.subtract(WholeHundred[1]);
				}
			} else {
				log.info("投资金额计算结果为0==》投资人{},标的ID{},最小投资金额{},计算后投资金额{}",userConfig.getUserId(),project.getProjectId(),responseDTO.getStartInvestMoney(),minInvestAmount);
				return BigDecimal.ZERO;
			}
		}
		return minInvestAmount;
	}

	public Long invest(String userId, Long projectId, InvestWayEnum investWay, Long batchNo, AccountBaseResponse accountInfo, BigDecimal availableAmount, BigDecimal investAmount,
			InvestDubboRequest investReq) {
		Date expired = new DateTime().plusMinutes(10).toDate();
		InvestOrderResponseDTO investOrder = investService.invest(investReq, accountInfo.getAccountId(), availableAmount, expired, (byte) 1, investWay, batchNo, accountInfo.getAccountNo());
		InvestPayDubboRequestDTO req = new InvestPayDubboRequestDTO();
		req.setUserId(userId);
		req.setProjectId(projectId);
		req.setBorrowerUserId(investOrder.getBorrowUserId());
		req.setInvestAmount(investAmount);
		req.setInvestOrderId(investOrder.getInvestOrderId());
		req.setExpired(investOrder.getExpired());
		req.setProjectType(investOrder.getProjectType());
		req.setProjectTitle(investOrder.getProjectTitle());
		req.setDeadline(investOrder.getDeadline());
		req.setDeadlineUnit(investOrder.getDeadlineUnit());
		accountInvestDubboService.accountInvest(req);
		return investOrder.getInvestOrderId();
	}

	/**
	 * 查询可投资的标的
	 * 
	 * @param userId
	 * @param productTypes
	 * @param repaymentTypes
	 * @return
	 */
	public List<ProjectInvesting> queryAllInvestingProjectWithCondition(String userId, List<Integer> productTypes, List<Integer> repaymentTypes) {

		return projectInvestingMapperExt.queryAllInvestingProjectWithCondition(userId, productTypes, repaymentTypes);
	}

	/**
	 * 休息区的人员验证返回投资区，并将上标时间超过十分钟的重新投入自动投资队列
	 */
	public void revertAutoInvest(Long time) {
		try {
			// 资金不足的队列验证，通过的返回回去，不通过继续停留
			reverNotSufficientFunds(time);
		} catch (Exception e) {
			log.error("资金不足休息区，用户回归异常",e);
		}
		try {
			// 必须使用红包没有红包的校验，通过近投资队列，不通过继续等待
			reverNoRedEnvelopes(time);
		} catch (Exception e) {
			
		}
		try {
			// 风险投资可有金额不足
			reverRiskFund(time);
		} catch (Exception e) {
			log.error("风险额度休息区，用户回归异常",e);
		}
		
		// 位置异常的无条件返回
//		reverUnknow(time);
	}

	/**
	 * 位置异常的，配置生效的情况无条件返回
	 * 
	 * @param tenMinuterAgo
	 */
	private void reverUnknow(Long tenMinuterAgo) {
		BoundZSetOperations<String, String> opsZset = redisTemplate.boundZSetOps(TradeRedisKey.REST_AREA_UNKNOW);
		// 获取十分钟前放入队列的用户
		Set<String> users = opsZset.rangeByScore(0, tenMinuterAgo);
		log.info("自动投资==>特殊异常休息区{}人员。", Objects.isNull(users) ? 0 : users.size());
		// 特殊异常休息区无人，不处理
		if (CollectionUtils.isEmpty(users)) {
			return;
		}
		for (String userId : users) {
			UserInvestConfig userInvestConfig = ((AutoBatchInvestService)AopContext.currentProxy()).queryUserInvestConfigForMaster(userId, InvestWayEnum.AUTO);
			// 自动投资生效，进队列
			if ((validateConfig(userInvestConfig))) {
				userIntoAutoQueue(userId, userInvestConfig.getWeight(), userInvestConfigDetailMapperExt.findByUserInvestConfigId(userInvestConfig.getId()));
			}
			opsZset.remove(userId);
		}
	}

	/**
	 * 验证风险投资金额是否充足
	 * 
	 * @param tenMinuterAgo
	 */
	private void reverRiskFund(Long tenMinuterAgo) {
		BoundZSetOperations<String, String> opsZset = redisTemplate.boundZSetOps(TradeRedisKey.REST_AREA_RISK_FUND);
		// 获取十分钟前放入队列的用户
		Set<String> users = opsZset.rangeByScore(0, tenMinuterAgo);
		log.info("自动投资==>风险投资资金不足休息区{}人员。", Objects.isNull(users) ? 0 : users.size());
		// 资金不足休息区无人，不处理
		if (CollectionUtils.isEmpty(users)) {
			return;
		}
		for (String userId : users) {
			try {
				UserInvestConfig userInvestConfig = ((AutoBatchInvestService)AopContext.currentProxy()).queryUserInvestConfigForMaster(userId, InvestWayEnum.AUTO);
				// 自动投资配置如果无效移除休息区
				if ((!validateConfig(userInvestConfig))) {
					log.info("风险额度休息区,用户{}配置已变更无效，删除休息区",userId);
					opsZset.remove(userId);
					continue;
				}
				// 获取当前风险等级对应的剩余可投资金额.
				BigDecimal surplusCanInvestAmount = investCommonServer.getSurplusCanInvestAmount(userId);
				// 风险剩余可投金额小于当前自小投资金额，不处理
				if (hundred.compareTo(surplusCanInvestAmount) > 0) {
					log.info("风险额度休息区，用户{},风险额度为{},继续停留休息区",userId,surplusCanInvestAmount);
					continue;
				}
				userIntoAutoQueue(userId, userInvestConfig.getWeight(), userInvestConfigDetailMapperExt.findByUserInvestConfigId(userInvestConfig.getId()));
				opsZset.remove(userId);
			} catch (Exception e) {
				log.error("风险资金额度休息区,用户{}回归出现异常",userId,e);
			}
			
		}

	}

	/**
	 * 验证自动投资用户是否还是必须使用红包，是否投资账户余额高于红包最低使用金额
	 * 
	 * @param tenMinuterAgo
	 */
	private void reverNoRedEnvelopes(Long tenMinuterAgo) {
		BoundZSetOperations<String, String> opsZset = redisTemplate.boundZSetOps(TradeRedisKey.REST_AREA_NO_RED_ENVELOPES);
		// 获取十分钟前放入队列的用户
		Set<String> users = opsZset.rangeByScore(0, tenMinuterAgo);
		log.info("自动投资==>必须使用红包队列休息区{}人员。", Objects.isNull(users) ? 0 : users.size());
		// 资金不足休息区无人，不处理
		if (CollectionUtils.isEmpty(users)) { 
			return;
		}
		for (String userId : users) {
			try {
				AccountBaseResponse accountBaseResponse = accountDubboService.getNiiwooAccount(userId, UserRoleEnum.INVESTOR);
				UserInvestConfig userInvestConfig = ((AutoBatchInvestService)AopContext.currentProxy()).queryUserInvestConfigForMaster(userId, InvestWayEnum.AUTO);
				UserInvestPrizeConfig investPrizeConfig = userInvestPrizeConfigMapperExt.queryByUserId(userId, InvestWayEnum.AUTO.getValue());
				// 自动投资配置如果无效移除休息区
				if (Objects.isNull(accountBaseResponse) || (!validateConfig(userInvestConfig)) || Objects.isNull(investPrizeConfig)) {
					log.info("用户{},红包休息区回归时配置信息无效，删除休息区",userId);
					opsZset.remove(userId);
					continue;
				}
				// 当前配置必须使用红包校验，否则不校验
				if (Byte.valueOf("1").equals(investPrizeConfig.getIsMustUsePrize())) {
					boolean b = investPrizeService.valdatePrize(userId, investPrizeConfig.getUseCouponMinInvestAmount(), accountBaseResponse.getAvailableAmount());
					log.info("用户{},余额{},加息劵最低使用金额{},红包休息区当前配置还是必须使用红包，当时没有红包或者最低达不到最低加息劵使用金额",userId,accountBaseResponse.getAvailableAmount(),investPrizeConfig.getUseCouponMinInvestAmount());
					if (!b) {
						continue;
					}
				}
				userIntoAutoQueue(userId, userInvestConfig.getWeight(), userInvestConfigDetailMapperExt.findByUserInvestConfigId(userInvestConfig.getId()));
				opsZset.remove(userId);
			} catch (Exception e) {
				log.error("红包休息区出现异常，用户{}",userId,e);
			}
			
		}
	}

	/**
	 * 验证自动投资用户资金是否充足
	 * 
	 * @param tenMinuterAgo
	 */
	private void reverNotSufficientFunds(Long tenMinuterAgo) {
		BoundZSetOperations<String, String> opsZset = redisTemplate.boundZSetOps(TradeRedisKey.REST_AREA_NOT_SUFFICIENT_FUNDS);
		// 获取十分钟前放入队列的用户
		Set<String> users = opsZset.rangeByScore(0, tenMinuterAgo);
		log.info("自动投资==>资金不足休息区{}人员。", Objects.isNull(users) ? 0 : users.size());
		// 资金不足休息区无人，不处理
		if (CollectionUtils.isEmpty(users)) {
			return;
		}
		// 最低保留金额
		BigDecimal minKeepBalance = BigDecimal.ZERO;
		for (String userId : users) {
			try {
				AccountBaseResponse accountBaseResponse = accountDubboService.getNiiwooAccount(userId, UserRoleEnum.INVESTOR);
				UserInvestConfig userInvestConfig = ((AutoBatchInvestService)AopContext.currentProxy()).queryUserInvestConfigForMaster(userId, InvestWayEnum.AUTO);
				// 自动投资配置如果无效移除休息区
				if (Objects.isNull(accountBaseResponse) || (!validateConfig(userInvestConfig))) {
					log.info("用户{}自动投资配置变更，不符合自动投资配置，移除休息区",userId);
					opsZset.remove(userId);
					continue;
				}
				// 最小保留金额
				minKeepBalance = Objects.isNull(userInvestConfig.getMinKeepBalance()) ? BigDecimal.ZERO : userInvestConfig.getMinKeepBalance();
				// 投资可用余额-自动投资最低保留金额<100 资金不足继续停留休息区
				if (accountBaseResponse.getAvailableAmount().subtract(minKeepBalance).compareTo(hundred) < 0) {
					log.info("资金不足休息区用户{},当前可用余额{},最低保留金额{},继续等待资金充足",userId,accountBaseResponse.getAvailableAmount(),minKeepBalance);
					continue;
				}
				userIntoAutoQueue(userId, userInvestConfig.getWeight(), userInvestConfigDetailMapperExt.findByUserInvestConfigId(userInvestConfig.getId()));
				opsZset.remove(userId);
			} catch (Exception e) {
				log.error("资金不足休息区，用户{}回归时出现特殊异常",userId,e);
			}
			
		}

	}

	/**
	 * 校验当前的配置是否还在启用
	 * 
	 * @param userInvestConfig
	 * @return
	 */
	private boolean validateConfig(UserInvestConfig userInvestConfig) {

		// 配置不存在，自动投资已关闭，自动投资被系统关闭，符合一个都是false
		return !(Objects.isNull(userInvestConfig) || Byte.valueOf("0").equals(userInvestConfig.getEnableAuto()) || Byte.valueOf("1").equals(userInvestConfig.getEnableSystem()));
	}

	/**
	 * 将可投资的标放入自动投资队列
	 */
	public void revertProject(Long time) {

		List<ProjectInvesting> projects = projectInvestingMapperExt.selectAllWithPublicTime(new Date(time));
		if (CollectionUtils.isNotEmpty(projects)) {
			for (ProjectInvesting project : projects) {
				rabbitTemplate.convertAndSend(RabbitConstant.Exchange.AUTO_INVEST_PROJECT, RabbitConstant.RoutingKey.AUTO_INVEST_PROJECT, project.getProjectId());
			}
			
		}
	}
	/**
	 * 修改系统关闭改为已阅
	 * @param userId
	 */
	@Transactional
	public void updateReadFalg(String userId) {
		UserInvestConfig config = new UserInvestConfig();
		config.setUserId(userId);
		config.setInvestWay(InvestWayEnum.AUTO.getValue());
		config.setReadFlag(Byte.valueOf("1"));//已读
		userInvestConfigMapperExt.updateByUserId(config);
	}

	/**
	 * 自动投资历史数据初始化
	 */
	@Transactional
	public void dataTransfer() {
		long time = System.currentTimeMillis();
		log.info("自动投资配置初始化迁移开始======");
		List<SysInvestConfig> sysInvestConfigs = sysInvestConfigMapperExt.selectAll();
		if (CollectionUtils.isEmpty(sysInvestConfigs)) {
			log.info("自动投资配置初始化迁移,查询无数据，退出");
			return;
		}
		// 符合利率大于等于0.9的都符合
		Map<String, SysInvestConfig> enableConfig = new HashMap<>();
		// 最小利率
		BigDecimal minRate = BigDecimal.valueOf(0.09);
		for (SysInvestConfig sysInvestConfig : sysInvestConfigs) {
			if (minRate.compareTo(sysInvestConfig.getMaxInvestRate()) <= 0) {
				enableConfig.put(sysInvestConfig.getId() + "", sysInvestConfig);
			}
		}
		List<UserInvestConfig> user = userInvestConfigMapperExt.queryAllUserInvestConfig(InvestWayEnum.AUTO.getValue());
		if (CollectionUtils.isEmpty(user)) {
			log.info("自动投资配置初始化迁移,无开启自动投资的用户，退出");
			return;
		}
		userInvestConfigDetailMapperExt.deleteAll();
		log.info("自动投资配置初始化迁移,开启自动投资用户数{}",user.size());
		String[] configs = null;
		Set<BigDecimal> userRate = null;
		Set<Integer> userPeriod = null;
		SysInvestConfig userConfig = null;
		// 利率区间
		BigDecimal rateSection1 = BigDecimal.valueOf(0.105);
		BigDecimal rateSection2 = BigDecimal.valueOf(0.12);
		BigDecimal rateSection3 = BigDecimal.valueOf(0.13);
		BigDecimal rateSection4 = BigDecimal.valueOf(0.15);
		//默认全选三个产品 信用卡贷、社保贷、房抵贷
		List<Integer> productType = new ArrayList<>();
		productType.add(LoanTypeEnum.SOCIAL_SECURITY_LOAN.getValue().intValue());
		productType.add(LoanTypeEnum.CREDIT_CARD_LOAN.getValue().intValue());
		productType.add(LoanTypeEnum.HOUSE_MORTGAGE_LOAN.getValue().intValue());
		productType.add(LoanTypeEnum.SPEED_LOAN.getValue().intValue());
		List<Integer> repaymentType = new ArrayList<>();
		//默认选择先息后本和等额本息
		repaymentType.add(RepaymentTypeEnum.AVERAGE_CAPITAL_PLUS_INTEREST.getValue().intValue());
		repaymentType.add(RepaymentTypeEnum.MONTHLY_INTEREST_PAYMENT_DUE.getValue().intValue());
		BigDecimal userMaxRate = null;
		int i = 0;
		for (UserInvestConfig userInvestConfig : user) {
			i++;
			log.info("自动投资配置初始化迁移,当前第{}个，用户ID{}",i,userInvestConfig.getUserId());
			userRate = new HashSet<>();
			userPeriod = new HashSet<>();
			if(StringUtils.isBlank(userInvestConfig.getConfigIds())) {
				continue;
			}
			// ID分组
			configs = userInvestConfig.getConfigIds().split(",");
			for (String id : configs) {
				// 利率大于0.9
				if (Objects.isNull(userConfig = enableConfig.get(id.trim()))) {
					continue;
				}
				userMaxRate = userConfig.getMaxInvestRate();
				// 最大值小于0.105使用0.9
				if (rateSection1.compareTo(userMaxRate) > 0) {
					userRate.add(minRate);
					// 最大值小于0.12，使用0.105
				} else if (rateSection2.compareTo(userMaxRate) > 0) {
					userRate.add(rateSection1);
					// 最大值小于0.13，使用0.12
				} else if (rateSection3.compareTo(userMaxRate) > 0) {
					userRate.add(rateSection2);
					// 最大值小于0.15，使用0.13
				} else if (rateSection4.compareTo(userMaxRate) > 0) {
					userRate.add(rateSection3);
					//最大0.15
				} else {
					userRate.add(rateSection4);
				}
				userPeriod.add(userConfig.getMaxDeadline());

			}
			//没有符合的配置 关闭用户信息
			if(CollectionUtils.isEmpty(userRate)) {
				userInvestConfigMapperExt.disableAutoInvestBySystemUserId(userInvestConfig.getUserId());
				continue;
			}
			Long  userInvestConfigId= userInvestConfig.getId();
			//产品
			insertUserInvestConfigDetail(userInvestConfigId, productType, UserInvestConfigDetailTypeEnum.PRODUCT_TYPE.getValue());
			//还款方式
			insertUserInvestConfigDetail(userInvestConfigId, repaymentType, UserInvestConfigDetailTypeEnum.REPAYMENT_TYPE.getValue());
			//利率
			List<BigDecimal> rates = new ArrayList<>();
			rates.addAll(userRate);
			insertUserInvestConfigDetailRateType(userInvestConfigId, rates, UserInvestConfigDetailTypeEnum.RATE_TYPE.getValue());
			
			for (Integer period : userPeriod) {
				UserInvestConfigDetail configDetail = new UserInvestConfigDetail();
				configDetail.setId(snowflakeIdWorker.nextId());
				configDetail.setConfigType( UserInvestConfigDetailTypeEnum.PERIOD_TYPE.getValue());
				configDetail.setCreateTime(new Date());
				configDetail.setUpdateTime(new Date());
				configDetail.setUserInvestConfigId(userInvestConfigId);
				configDetail.setConfigValue(period.toString());
				configDetail.setOtherConfigValue(DeadlineUnitEnum.BY_MONTH.getDeadlineUnit().toString());
				userInvestConfigDetailMapperExt.insertSelective(configDetail);
			}
			
		}
		log.info("自动投资配置初始化迁移结束，耗时{}",System.currentTimeMillis()-time);
	}
	 

}
